-- Final version of Switch Fabric
-- written by: David Wagner
--
-- 5/4 9:00PM Multiple cycle verification complete
--		FabricRdy signal fed back to the PMLUT component.
--
-- 5/4 2:55PM VLAN tag and CRC check bits included into the system and rechecked
--		4:350PM Single cycel verification by simulation is complete.
--
-- 5/4 12:30AM Resimulated for specific test cases 
--
-- 5/3 2:00PM Modified by David Wagner
-- Reinserted clock-sensitive RESET state (note: this is *essential* for interpreting the LUT's data valid pin)
-- Cleaned up a few states, so logic is easier to read and understand
-- Added some logic to setting the broadcast bit of the output pointer (bit 0) appropriately
-- Simulated; verified correct operation
--
-- 5/1 7:30PM Modifications made by Vincent Mao
-- Added extra case for Port Dest = "00" for testing purposes
	-- directed to drop if this is true
	-- moved reset state to before clock sensitive portion
	-- specified more outputs at reset and first state
	

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;

ENTITY fabric IS
      PORT ( Clock 		: 	IN      STD_LOGIC ;
			 RESETN		:	IN		STD_LOGIC ;
			 Bcast		:	IN		STD_LOGIC ;
			 LookUpRdy	:	IN		STD_LOGIC ; 	-- port map block has data ready
			
			 PtrIN		: 	IN	  	STD_LOGIC_VECTOR (8 DOWNTO 0) ;
			 PortDest	:	IN		STD_LOGIC_VECTOR (1 DOWNTO 0) ;	
			 PortSrc	:	IN		STD_LOGIC_VECTOR (1 DOWNTO 0) ;
			 VlanTag	:	IN		STD_LOGIC_VECTOR (31 DOWNTO 0);
			 TagValid	:	IN		STD_LOGIC ;
					
			 XmtReady	:	IN		STD_LOGIC ;		-- xmt block 1 ready to receive data 
						
			 FabricRdy	:	OUT		STD_LOGIC ;		-- ready for port map block
			 DataValid1 :	OUT		STD_LOGIC ;		-- data ready for xmt block 1
			 DataValid2 :	OUT		STD_LOGIC ;		-- data ready for xmt 2
			 DataValid3 :	OUT		STD_LOGIC ;		-- "		"	  xmt 3
			 Drop		:	OUT		STD_LOGIC ;		-- data valid for drop block
			 PtrOut		:	OUT		STD_LOGIC_VECTOR (8 DOWNTO 0) ;		-- pointer going to xmt block 1
			 VlanTagOut	:	OUT		STD_LOGIC_VECTOR (31 DOWNTO 0) ;	-- VLAN Tag to be inserted (block 1)
			 TagValidOut:	OUT		STD_LOGIC 		-- add tag, re-calc CRC (1)
			);
END fabric;

ARCHITECTURE Behavior OF fabric IS
	TYPE State_type IS (RESET, WAITFORNEW, TESTBCAST, WaitForXmtBcast, WaitForXmtDrop, WaitForXmtSingle, OUTPUT );
	SIGNAL State : State_type;
	
	SIGNAL	 M_Bcast	:	STD_LOGIC ;						-- local broadcast designation
	SIGNAL	 M_Ptr		:	STD_LOGIC_VECTOR (8 DOWNTO 0) ; -- local frame pointer
	SIGNAL	 M_PortDest :	STD_LOGIC_VECTOR (1 DOWNTO 0) ;	-- local port destination
	SIGNAL	 M_PortSrc	:	STD_LOGIC_VECTOR (1 DOWNTO 0) ; -- local port source
	SIGNAL	 M_TagValid	:	STD_LOGIC; 						-- local CRC check
	SIGNAL	 M_VLANTag	:	STD_LOGIC_VECTOR (31 DOWNTO 0) ; -- local VLAN Tag
	
BEGIN
	-- Initialize

	PROCESS (Clock, RESETN)
	VARIABLE suppressBcast	: STD_LOGIC_VECTOR (2 DOWNTO 0) := "000" ; -- represents ports to suppress on broadcasts
	VARIABLE toBcast	 	: STD_LOGIC_VECTOR (2 DOWNTO 0) ; -- ports yet to be broadcast to
	BEGIN
		IF (RESETN = '1') THEN
				--Not sending any data
					DataValid1	<= '0';
					DataValid2	<= '0';
					DataValid3	<= '0';
					Drop		<= '0';
					PtrOut		<= (OTHERS => '0');
					VlanTagOut	<= (OTHERS => '0');
					TagValidOut <= '0';
					FabricRdy	<= '1';
					
					State		<= RESET;

			ELSIF (Clock'EVENT AND Clock = '1') THEN
				CASE State IS

					WHEN RESET =>
						DataValid1	<= '0';
						DataValid2	<= '0';
						DataValid3	<= '0';
						Drop		<= '0';					
						PtrOut		<= (OTHERS => '0');
						VlanTagOut	<= (OTHERS => '0');
						TagValidOut <= '0';
						FabricRdy	<= '1';
						IF (LookUpRdy = '0') THEN
							State <= WAITFORNEW;
						END IF;
					
														
					WHEN WAITFORNEW =>
						--Not sending any data
						
						IF LookUpRdy = '1' THEN
							M_Bcast 	<= Bcast;
							M_Ptr		<= PtrIN;
							M_PortDest	<= PortDest;
							M_PortSrc	<= PortSrc;
							M_TagValid	<= TagValid;
							M_VLANTag	<= VlanTag;
							FabricRdy	<= '0';
							
							State			<= TESTBCAST;
						END IF;
						
					WHEN TESTBCAST =>
						IF M_Bcast = '1' THEN
							CASE M_PortSrc IS
								WHEN "01" =>
									toBcast := "110";
								WHEN "10" =>
									toBcast := "101";
								WHEN "11" =>
									toBcast := "011";
								WHEN "00" =>							-- RSTP and BPDU special cases
									toBcast := "111";
							END CASE;

							toBcast := toBcast AND NOT suppressBcast;	-- remove ports suppressed by VLAN
							
							IF (XmtReady = '1') THEN
								DataValid1 <= toBcast(0);
								DataValid2 <= toBcast(1);
								DataValid3 <= toBcast(2);

								IF (toBcast = "000") THEN	-- no broadcasting employed
									Drop <= '1';
								END IF;
								
								State <= OUTPUT;
							ELSE
								State <= WaitForXmtBcast;
							END IF;

						ELSIF M_PortDest = M_PortSrc THEN
							IF XmtReady = '1' THEN	
								Drop <= '1';
								State <= OUTPUT;
							ELSE
								State <= WaitForXmtDrop;
							END IF;
						ELSIF M_PortDest = "01" THEN
							IF XmtReady = '1' THEN	
								DataValid1 <= '1';
								State <= OUTPUT;
							ELSE
								State <= WaitForXmtSingle;
							END IF;
						ELSIF M_PortDest = "10" THEN
							IF XmtReady = '1' THEN	
								DataValid2 <= '1';
								State <= OUTPUT;
							ELSE
								State <= WaitForXmtSingle;
							END IF;
						ELSIF M_PortDest = "11" THEN
							IF XmtReady = '1' THEN	
								DataValid3 <= '1';
								State <= OUTPUT;
							ELSE
								State <= WaitForXmtSingle;
							END IF;
						ELSIF M_PortDest = "00" THEN
							IF XmtReady = '1' THEN
								DataValid1 <= '0';
								DataValid2 <= '0';
								DataValid3 <= '0';
								Drop <= '1';
								State <= OUTPUT;
							ELSE
								State <= WaitForXmtSingle;
							END IF;
						END IF;

					WHEN WaitForXmtBcast =>
						IF (XmtReady = '1') THEN
							DataValid1 <= toBcast(0);
							DataValid2 <= toBcast(1);
							DataValid3 <= toBcast(2);

							IF (toBcast = "000") THEN	-- no broadcasting employed
								Drop <= '1';
							END IF;
							
							State <= OUTPUT;
						ELSE
							State <= WaitForXmtBcast;
						END IF;
						
					WHEN WaitForXmtDrop =>
						IF XmtReady = '1' THEN
							Drop <= '1';
							State <= OUTPUT;
						ELSE
							State <= WaitForXmtDrop;
						END IF;
						
					WHEN WaitForXmtSingle =>
						IF XmtReady = '1' THEN
							IF M_PortDest = "01" THEN
								DataValid1 <= '1';
								State <= OUTPUT;
							ELSIF M_PortDest = "10" THEN
								DataValid2 <= '1';
								State <= OUTPUT;
							ELSIF M_PortDest = "11" THEN
								DataValid3 <= '1';
								State <= OUTPUT;
							ELSIF M_PortDest = "00" THEN
								DataValid1 <= '0';
								DataValid2 <= '0';
								DataValid3 <= '0';
								Drop <= '1';
								State <= OUTPUT;
							END IF;
						END IF;
					
					WHEN OUTPUT =>
						PtrOut(8 DOWNTO 1) <= M_Ptr(8 DOWNTO 1);
						-- set LSB of pointer if *more than one* port will use this data
						IF (M_Bcast = '1' AND (toBcast="011" OR toBcast="101" OR toBcast="110" OR toBcast="111")) THEN
							PtrOut(0) <= '1';
						ELSE
							PtrOut(0) <= '0';
						END IF;
						TagValidOut <= M_TagValid;
						VLANTagOut	<= M_VLANTag;
						FabricRdy 	<= '1';						
						State 		<= RESET;						
					END CASE; -- case for state (State)
				END IF; -- clock edge

	END PROCESS ;
END Behavior ;